import {UnitModel} from "../frame/apiModel/adapterModel/model3/UnitModel";
import {AllUnitAttrs, Attrs, CaluType, ColorStr, MainAttr, SpecialEffectType} from "../type/Type";
import {AttributeEnum, HurtType, ItemData, UnitAttribute, UnitType} from "../frame/dataSource/DataModel";
import {PlayerDataKey, UnitDataKey} from "../frame/dataSource/DataSource";
import {DataRouter} from "../frame/dataSource/DataRouter";
import {Util} from "./Util";
import {CodeUtil} from "../frame/codeAdapter/CodeUtil";
import {PlayerFactory} from "../frame/PlayerFactory";
import {Logger} from "../frame/apiModel/config/Logger";
import {PlayerModel} from "../frame/apiModel/adapterModel/model4/PlayerModel";
import Configer from "../frame/apiModel/config/Configer";
import {AttrSetting, ConfigKey} from "../constant/ConfigConstant";
import MathUtil from "../frame/codeAdapter/MathUtil";
import {TimeController} from "../controller/TimeController";
import {ModelFactory} from "../frame/apiModel/adapterModel/model5/ModelFactory";
import {DataRouterUtil} from "./router/DataRouterUtil";
import {GlobalEnv} from "./GlobalEnv";
import {LocModel} from "../frame/apiModel/base/LocModel";
import {ResourceKey} from "../constant/ResourceConstant";


export enum UseAbilityFailedType {
    noMana = 1,
    noCD = 2,
    noAch,
}

export default class UnitUtil {
    static attackCallBack: ((attacker: UnitModel, attacked: UnitModel, bCh: boolean, bHit: boolean) => void)[] = [];

    static getVestJson(vest: UnitModel) {
        return vest.data[UnitDataKey.vestJson];
    }
    // 修改单位绿字的接口
    static getExtraAttr(unit: UnitModel) {
        if (unit) {
            const extraAttr = unit.data[UnitDataKey.extraAttribute];
            if (extraAttr) {
                return extraAttr;
            }
        }
        return new UnitAttribute();
    }

    static addSubAttr(unit: UnitModel, unitAttr: UnitAttribute) {
        const subAttr = unit.data[UnitDataKey.subAttribute];
        if (subAttr) {
            unit.data[UnitDataKey.subAttribute] = Util.sumUnitAttribute(subAttr, unitAttr, CaluType.add);
        }
    }

    static getSubAttr(unit: UnitModel) {
        const subAttr = unit.data[UnitDataKey.subAttribute];
        if (subAttr) {
            return subAttr;
        }
        return new UnitAttribute();
    }

    static reflushAttribute(unit: UnitModel, attr: UnitAttribute, includeBonuses: boolean) {
        if (attr) {
            const keys = Object.keys(attr);
            for (let i = 0; i < keys.length; i ++) {
                const key = keys[i] as string;
                const numKey = MathUtil.toNumber(key);
                let value;
                if (unit?.isHero()) {
                } else {
                    switch (numKey) {
                        case AttributeEnum.strength:
                        case AttributeEnum.agile:
                        case AttributeEnum.intellect:{
                            continue;
                        }
                    }
                }
                switch (numKey) {
                    // 其他属性不会产生框架外的变化，不需要更新
                    case AttributeEnum.strength:
                    case AttributeEnum.agile:
                    case AttributeEnum.intellect:
                    case AttributeEnum.life:
                    case AttributeEnum.maxLife:
                    case AttributeEnum.magic:
                    case AttributeEnum.maxMagic:
                    // case AttributeEnum.attack:
                    case AttributeEnum.attackSpeed:
                    case AttributeEnum.speed:
                    case AttributeEnum.coin:
                    case AttributeEnum.wood:
                    case AttributeEnum.exp:
                    case AttributeEnum.attackDistance: {
                        value = Util.getTargetAttributeEnumByNotHero(unit, numKey, includeBonuses);
                        if (value) {
                            attr[key] = value;
                        }
                    }
                }
            }
            return attr;
        }
    }

    /**
     * attribute不提前注册，在获取时如果取不到就取默认的
     * 对于英雄来说这个属性会在第一次调用caluAttribute()后注册
     * 当前额外属性: 额外属性只在unitController中使用，主要用来和增加的额外属性一起计算属性系统增加前的属性
     * 增加的额外属性: 增加的额外属性只在unitController中使用，主要用来和当前额外属性一起计算属性系统增加前的属性
     * 被削减的属性: 被消减的属性指的是在使用技能或物品永久降低某项属性时，从这个属性中减
     * 最新属性: 当前单位的最新属性，从当前额外属性中获得，并且更新其中会被其他因素影响的值，比如三元
     */
    static getAttribute(unit: UnitModel): AllUnitAttrs {
        const ret: { newestAttribute: UnitAttribute, extraAttr: UnitAttribute, subExtraAttr: UnitAttribute, whiteAttr: UnitAttribute, originAttr: UnitAttribute } = { newestAttribute: undefined, extraAttr: undefined, subExtraAttr: undefined, whiteAttr: undefined, originAttr: undefined }
        if (unit?.isValid()) {
        } else {
            return ret;
        }

        ret.extraAttr = UnitUtil.getExtraAttr(unit);
        ret.subExtraAttr = UnitUtil.getSubAttr(unit);
        ret.whiteAttr = UnitUtil.reflushAttribute(unit, new UnitAttribute(), false);

        if (unit.data[UnitDataKey.newestAttribute]) {
            ret.newestAttribute = unit.data[UnitDataKey.newestAttribute];
        } else {
            ret.newestAttribute = new UnitAttribute();
        }

        UnitUtil.reflushAttribute(unit, ret.newestAttribute, true);

        // const strId = unit.strId;
        // const unitData = DataRouter.getUnitDataByUnitId(strId);
        // if (unitData) {
        //     const mainAttr = unitData.Primary;
        //     Util.attackAdapt(unit, mainAttr, ret.newestAttribute, ret.extraAttr);
        // }

        ret.originAttr = Util.sumUnitAttribute(ret.newestAttribute, ret.extraAttr, CaluType.sub);

        return ret;
    }

    static getItemFightValue(itemData: ItemData, affixStr: string): number {
        if (itemData) {
            let itemAttr = DataRouterUtil.getAttrByAttrInterface(itemData?.attrId, 1);
            const itemAffixAttr = UnitUtil.getItemAffixAttr(affixStr);
            itemAttr = Util.sumAttributes(itemAttr, itemAffixAttr, CaluType.add);
            const value = UnitUtil.getFightValueByAttr(itemAttr.unitAttr);
            return value;
        }
        return 0;
    }

    static getItemAffixAttr(affixStr: string): Attrs {
        let attrs: Attrs = { unitAttr: new UnitAttribute(), playerAttr: undefined };
        if (!CodeUtil.isNullOrZero(affixStr)) {
            const affixArr = CodeUtil.getArrBySplit(affixStr);
            for (let i = 0; i < affixArr.length; i ++) {
                const affixId = affixArr[i];
                const affixData = DataRouter.getAffixDatasByAffixID(affixId);
                const affixAttr = DataRouterUtil.getAttrByAttrInterface(affixData?.attrId, 1);
                attrs = Util.sumAttributes(attrs, affixAttr, CaluType.add);
            }
        }
        return attrs;
    }

    static getFightValueByAttr (attackerAttribute: UnitAttribute): number {
        let value = 0;
        const attrSetting = Configer.getConfig(ConfigKey.attrSetting);
        if (attackerAttribute) {
            const keys = Object.keys(attackerAttribute);
            for (let i = 0; i < keys.length; i ++) {
                const key = keys[i];
                const attrValue = attackerAttribute[key];
                const fightBase = attrSetting[key] as AttrSetting;
                const fightValue = fightBase?.fightValue;
                if (!CodeUtil.isNullOrZero(attrValue) && !CodeUtil.isNullOrZero(fightValue)) {
                    value = value + attrValue * fightValue;
                }
            }
        }
        value = MathUtil.floor(value);
        return value;
    }

    static getUnitHurt (unit: UnitModel, attack: number, attackerAttribute: UnitAttribute, attackedAttribute: UnitAttribute, type: HurtType):
        { hurt: number, bCh: boolean, bHit: boolean, bBlock: boolean, bloodSuck: number, thorns: number, realH: number  }
    {
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        const ret: { hurt: number, bCh: boolean, bHit: boolean, bBlock: boolean, bloodSuck: number, thorns: number, realH: number } = { hurt: 0, bCh: false, bHit: false, bBlock: false, bloodSuck: 0, thorns: 0, realH: 0 };

        if (CodeUtil.isNullOrZero(attack)) {
            return ret;
        }

        ret.hurt = attack;
        let chChance, chHurt, addRadix, hit, attackedDodge, attackedDamageReduction, blood_sucking, penetration, thorns, defense, regularPenetration, regularDamageReduction, damageBlock, precise, chResistance;



        const al_dodge = attackedAttribute[AttributeEnum.al_dodge];
        const al_damageBlock = attackedAttribute[AttributeEnum.al_damageBlock];
        const al_chResistance = attackedAttribute[AttributeEnum.al_chResistance];
        const al_thorns = attackedAttribute[AttributeEnum.al_thorns];
        const al_regularDamageReduction = attackedAttribute[AttributeEnum.al_regularDamageReduction];
        const al_damageReduction = attackedAttribute[AttributeEnum.al_damageReduction];


        const al_chHurt = attackerAttribute[AttributeEnum.al_chHurt];
        const al_chChance = attackerAttribute[AttributeEnum.al_chChance];
        const al_hurtAddRadix = attackerAttribute[AttributeEnum.al_hurtAddRadix];
        const al_blood_sucking = attackerAttribute[AttributeEnum.al_blood_sucking];
        const al_hit = attackerAttribute[AttributeEnum.al_hit];
        const al_regular_Penetration = attackerAttribute[AttributeEnum.al_regular_Penetration];
        const al_penetration = attackerAttribute[AttributeEnum.al_penetration];
        const al_precise = attackerAttribute[AttributeEnum.al_precise];


        switch (type) {
            case HurtType.hero:
            case HurtType.physics:
                damageBlock = attackedAttribute[AttributeEnum.physicsDamageBlock] + al_damageBlock;
                attackedDodge = attackedAttribute[AttributeEnum.physics_Dodge] + al_dodge;
                attackedDamageReduction = attackedAttribute[AttributeEnum.physics_damage_reduction] + al_damageReduction;
                defense = attackedAttribute[AttributeEnum.defense];
                regularDamageReduction = attackedAttribute[AttributeEnum.regular_physics_damage_reduction] + al_regularDamageReduction;
                chResistance = attackedAttribute[AttributeEnum.CH_physics_Resistance] + al_chResistance
                thorns = attackedAttribute[AttributeEnum.physics_Thorns] + al_thorns;

                chChance = attackerAttribute[AttributeEnum.CH_physics_chance] + al_chChance;
                chHurt = attackerAttribute[AttributeEnum.CH_physics_hurt] + al_chHurt;
                addRadix  = attackerAttribute[AttributeEnum.physicsAddRadix] + al_hurtAddRadix;
                hit = attackerAttribute[AttributeEnum.physics_hit] + al_hit;
                blood_sucking = attackerAttribute[AttributeEnum.physics_blood_sucking] + al_blood_sucking;
                penetration = attackerAttribute[AttributeEnum.physics_Penetration] + al_penetration;
                regularPenetration = attackerAttribute[AttributeEnum.regular_physics_Penetration] + al_regular_Penetration
                precise = attackerAttribute[AttributeEnum.physicsPrecise] + al_precise;
                break;
            case HurtType.spell:
                damageBlock = attackedAttribute[AttributeEnum.spellDamageBlock] + al_damageBlock;
                attackedDodge = attackedAttribute[AttributeEnum.spell_Dodge] + al_dodge;
                attackedDamageReduction = attackedAttribute[AttributeEnum.spell_damage_reduction] + al_damageReduction;
                defense = attackedAttribute[AttributeEnum.magicResistance];
                regularDamageReduction = attackedAttribute[AttributeEnum.regular_spell_damage_reduction] + al_regularDamageReduction;
                chResistance = attackedAttribute[AttributeEnum.CH_spell_Resistance] + al_chResistance
                thorns = attackedAttribute[AttributeEnum.spell_Thorns] + al_thorns;

                chChance = attackerAttribute[AttributeEnum.CH_spell_chance] + al_chChance;
                chHurt = attackerAttribute[AttributeEnum.CH_spell_hurt] + al_chHurt;
                addRadix  = attackerAttribute[AttributeEnum.spellAddRadix] + al_hurtAddRadix;
                hit = attackerAttribute[AttributeEnum.spell_hit] + al_hit;
                blood_sucking = attackerAttribute[AttributeEnum.spell_blood_sucking] + al_blood_sucking;
                penetration = attackerAttribute[AttributeEnum.spell_Penetration] + al_penetration;
                regularPenetration = attackerAttribute[AttributeEnum.regular_spell_Penetration] + al_regular_Penetration;
                precise = attackerAttribute[AttributeEnum.spellPrecise] + al_precise;
                break;
            case HurtType.treat:
                penetration = attackerAttribute[AttributeEnum.treatPenetration];
                defense = attackedAttribute[AttributeEnum.treatResistance];
                attackedDamageReduction = attackedAttribute[AttributeEnum.treat_damage_reduction];
                chChance = attackerAttribute[AttributeEnum.CH_treat_chance];
                chHurt = attackerAttribute[AttributeEnum.CH_treat_hurt];
                addRadix  = attackerAttribute[AttributeEnum.treatAddRadix];
                regularPenetration = 0;
                chResistance = 0;
                break;
            default:
                chChance = 0;
                chHurt = 0;
                addRadix  = 0;
                hit = 0;
                attackedDodge = 0;
                attackedDamageReduction = 0;
                blood_sucking = 0;
                penetration = 0;
                thorns = 0;
                defense = 0;
                break;
        }

        // 计算是否暴击
        let random = player.getRandomInt(1, 100);
        chChance = chChance - chResistance;
        if (chChance > random) {
            ret.bCh = true;
            ret.hurt = ret.hurt * ((chHurt + 150) / 100);
        }
        Logger.toastSys(`暴击几率: ${chChance}`);
        Logger.toastSys(`暴击伤害: ${chHurt}`);
        Logger.toastSys(`暴击后伤害: ${ret.hurt}`);

        // 单属性伤害加成：物理？魔法？
        if (!CodeUtil.isNullOrZero(addRadix)) {
            ret.hurt = ret.hurt * (1 + addRadix/100);
        }
        Logger.toastSys(`攻击加成: ${ret.hurt}`)

        if (!CodeUtil.isNullOrZero(penetration)) {
            defense = defense * (1 - penetration/100);
        }
        Logger.toastSys(`百分比穿甲后护甲: ${defense}`)
        if (!CodeUtil.isNullOrZero(regularPenetration)) {
            defense = defense - regularPenetration;
        }
        Logger.toastSys(`固定穿甲后护甲: ${defense}`)

        if (defense < 0) {
            defense = 0;
        }

        const subValue = defense / (100 + defense);
        ret.hurt = ret.hurt * (1 - subValue);
        Logger.toastSys(`计算护甲后伤害: ${ret.hurt}`)

        // 计算伤害减免
        if (!CodeUtil.isNullOrZero(attackedDamageReduction)) {
            ret.hurt = ret.hurt * (1 - attackedDamageReduction/100);
        }
        Logger.toastSys(`伤害减免: ${ret.hurt}`)

        // 伤害大于0时才计算伤害
        if (ret.hurt > 0 && (type === HurtType.physics || type === HurtType.spell)) {
            // 计算是否命中
            const bHit = UnitUtil.caluDodge(player, hit + 100, attackedDodge);
            if (bHit) {
                ret.bHit = true;
            } else {
                ret.bHit = false;
                ret.hurt = 0;
                return ret;
            }

            const allAddRadix = attackerAttribute[AttributeEnum.allAddRadix];
            if (!CodeUtil.isNullOrZero(allAddRadix)) {
                ret.hurt = ret.hurt * (1 + allAddRadix/100);
            }
            Logger.toastSys(`护甲减伤: ${ret.hurt}`)

            if (!CodeUtil.isNullOrZero(regularDamageReduction)) {
                ret.hurt = ret.hurt - regularDamageReduction;
            }
            Logger.toastSys(`固定伤害减免: ${ret.hurt}`)

            const allDamageReduction = attackerAttribute[AttributeEnum.allDamageReduction];
            if (!CodeUtil.isNullOrZero(allDamageReduction)) {
                ret.hurt = ret.hurt * (1 - allDamageReduction/100);
            }

            Logger.toastSys(`全部伤害减免: ${ret.hurt}`)

            // 计算吸血
            ret.bloodSuck = ret.hurt * (blood_sucking / 100);
            // 计算反伤
            ret.thorns = ret.hurt * (thorns / 100);

            const bDamageLock = UnitUtil.caluDodge(player, precise + 100, damageBlock);
            if (bDamageLock) {
            } else {
                ret.bBlock = true;
                ret.hurt = 0;
                return ret;
            }
            Logger.toastSys(`格挡: ${ret.hurt}`)

            const realH = attackerAttribute[AttributeEnum.realHurt];
            if (!CodeUtil.isNullOrZero(realH)) {
                ret.hurt = ret.hurt + realH
            }
            Logger.toastSys(`真实伤害: ${ret.hurt}`)

            ret.realH = realH;
        }

        return ret;
    }

    static getCoolDown (cr: number, maxCool: number) {
        if (!CodeUtil.isNullOrZero(maxCool)) {
            if (!CodeUtil.isNullOrZero(cr)) {
                return (maxCool/(1+cr/100))
            }
            return maxCool;
        }
        return 0
    }

    static caluDodge(player: PlayerModel, hit: number, dodge: number) {
        if (!CodeUtil.isNullOrZero(dodge)) {
            // if (hit < 0) {
            //     hit = 0;
            // }
            // if (dodge < 0) {
            //     dodge = 0;
            // }
            const hitPro = hit / (hit + dodge);
            const newHitPro = hitPro * 100;
            const random = player.getRandomInt(0, 100);
            if (random < newHitPro) {
                return true;
            }
            return false;
        }
        return true;
    }

    getShotStrByNum(num: number) {
        let ret = `${num}`;
        let wan = MathUtil.floor(num / 10000);
        let yi = MathUtil.floor(num / 100000000);
        if (!CodeUtil.isNullOrZero(yi)) {
            ret = yi + 'y';
        } else if (!CodeUtil.isNullOrZero(wan)) {
            ret = wan + 'w';
        }
        return ret;
    }

    static showNumModelFun(sourceUnit: UnitModel, iconPath: ResourceKey, numPaths: ResourceKey, abbrPath: ResourceKey, numStr: string, fontSizeT: number, loc: LocModel, showTime: number, scaleRadix: number) {

        let tempNumStr: string = `${numStr} `;

        if (!CodeUtil.isNullOrZero(iconPath)) {
            const path = Configer.getResource(iconPath);
            // const iconScaleRadix = scaleRadix * 2;
            TimeController.addSpecialEffectFun(sourceUnit, path, showTime, loc, undefined, SpecialEffectType.point, scaleRadix);
        }

        const fontSize = 30;
        const len = tempNumStr.length;
        let bAbbrIndex = -1;
        if (!CodeUtil.isNullOrZero(numPaths)) {
            if (!CodeUtil.isNullOrZero(tempNumStr)) {
                const paths: string[] = Configer.getResource(numPaths);

                if (len > 9) {
                    tempNumStr = tempNumStr.slice(0, len - 9);
                    bAbbrIndex = 1;
                } else if (len > 5) {
                    tempNumStr = tempNumStr.slice(0, len - 5);
                    bAbbrIndex = 0;
                }

                for (let i = 0; i < tempNumStr.length; i ++) {
                    // 0 - 9
                    const char = tempNumStr[i];
                    // 0 - 9
                    const num = MathUtil.toNumber(char);
                    if (!CodeUtil.isNull(num)) {
                        const path = paths[num]
                        if (path) {
                            const distance = fontSize * (i + 1);
                            const tempLoc = loc.offset(distance, 0);
                            TimeController.addSpecialEffectFun(sourceUnit, path, showTime, tempLoc, undefined, SpecialEffectType.point, scaleRadix);
                        }
                    }
                }
            }
        }

        if (!CodeUtil.isNullOrZero(abbrPath)) {
            const paths: string[] = Configer.getResource(abbrPath);
            if (bAbbrIndex >= 0) {
                const path = paths[bAbbrIndex];
                const len = tempNumStr.length;
                const distance = fontSize * (len + 1);
                loc = loc.offset(distance, 0);
                TimeController.addSpecialEffectFun(sourceUnit, path, showTime, loc, undefined, SpecialEffectType.point, scaleRadix);
            }
        }
    }

    // todo 真实伤害与生命恢复需要对应的颜色模型
    static showHurtNum (hurtValue: number, attacker: UnitModel, attacked: UnitModel, hurtType: HurtType, bCh: boolean, bHit: boolean, bBlock: boolean, bloodSuck: number, thorns: number, realH: number) {
        const showHurt: number = Configer.getConfig(ConfigKey.showHurtNum);
        if (attacker?.isValid() && attacked?.isValid()) {
        } else {
            return;;
        }
        if (showHurt > 0) {
        } else {
            return;
        }
        const attackedLoc =  attacked.loc;
        let fontSize = 25;
        let scaleRadix = 0.7;
        const showTime = 0.1;
        let newHurtValue = 0, newBloodSuck = 0, newThornsStr = 0, newTureHurt = 0;
        if (bBlock) {
            const strId = Configer.getResource(ResourceKey.blockChars) as string;
            let loc = attackedLoc.offset(0, 0);
            TimeController.addSpecialEffectFun(attacker, strId, showTime, loc, undefined, SpecialEffectType.point, scaleRadix);
            return;
        }
        if (bHit) {
            newHurtValue = MathUtil.floor(hurtValue);
            newBloodSuck = MathUtil.floor(bloodSuck);
            newThornsStr = MathUtil.floor(thorns);
            newTureHurt = MathUtil.floor(realH)
        } else {
            const strId = Configer.getResource(ResourceKey.missChars) as string;
            let loc = attackedLoc.offset(0, 0);
            TimeController.addSpecialEffectFun(attacker, strId, showTime, loc, undefined, SpecialEffectType.point, scaleRadix);
            return;
        }

        if (newHurtValue < 0) {
            newBloodSuck = newBloodSuck - newHurtValue;
        }
        const str = newHurtValue + '';
        const bloodSuckStr = newBloodSuck + '';
        const tureHurtStr = newTureHurt + '';
        const thornsStr = newThornsStr + '';
        let arr: ResourceKey, abbrArr: ResourceKey, chArr:ResourceKey;
        const player = PlayerFactory.getInstance().getUnitOwner(attacker);
        if (player.isNotEndPlaying()) {
            if (hurtType === HurtType.physics) {
                arr = ResourceKey.physicsHurtChars;
                chArr = ResourceKey.chPhysicsHurtChars;
                abbrArr = ResourceKey.abbrPhysicsHurtChars;
            } else {
                arr = ResourceKey.spellHurtChars;
                chArr = ResourceKey.chSpellHurtChars;
                abbrArr = ResourceKey.abbrSpellHurtChars;
            }
        } else {
            // arr = Configer.getResource(ResourceKey.redHurtChars) as string[];
            // chArr = Configer.getResource(ResourceKey.chRedHurtChars) as string;
        }
        if (newHurtValue > 0) {
            Logger.toastSys(`伤害: ${hurtValue}`);

            let iconPath = bCh ? chArr : ResourceKey.default;
            UnitUtil.showNumModelFun(attacker, iconPath, arr, abbrArr, str, fontSize, attackedLoc, showTime, scaleRadix);
        }
        Logger.toastSys(`吸血: ${newBloodSuck}`);
        const lifePercent = attacker.lifePercent;
        if (!CodeUtil.isNullOrZero(newBloodSuck) && lifePercent < 100) {
            let iconPath = bCh ? ResourceKey.chSuckBloodChars : ResourceKey.default;
            UnitUtil.showNumModelFun(attacker, iconPath, ResourceKey.suckBloodChars, ResourceKey.abbrSuckBloodChars, bloodSuckStr, fontSize, attacker.loc, showTime, scaleRadix);
        }

        if (!CodeUtil.isNullOrZero(newTureHurt)) {
            let iconPath = bCh ? ResourceKey.chTureHurtChars : ResourceKey.default;
            UnitUtil.showNumModelFun(attacker, iconPath, ResourceKey.tureHurtChars, ResourceKey.abbrTureHurtChars, tureHurtStr, fontSize, attackedLoc, showTime, scaleRadix);
        }

        if (!CodeUtil.isNullOrZero(thorns)) {
            let iconPath = bCh ? ResourceKey.chTureHurtChars : ResourceKey.default;
            UnitUtil.showNumModelFun(attacked, iconPath, ResourceKey.tureHurtChars, ResourceKey.abbrTureHurtChars, thornsStr, fontSize, attacker.loc, showTime, scaleRadix);
        }
    }

    static correctHurt(attacker: UnitModel, attacked: UnitModel, hurtType: HurtType, hurt: number, damage: number, bloodSuck: number, bHit: boolean, thorns: number): number {
        let retHurt = hurt;
        // 法术不能使用英雄类型的伤害，以免与这里发生冲突
        if (attacker?.isAlive()) {
        } else {
            return retHurt;
        }
        if (attacked?.isAlive()) {
        } else {
            return retHurt;
        }

        // if (retHurt > attacked.life) {
        //     retHurt = attacked.life;
        // }

        const haflHurt = hurt - damage;

        // if (PlayerFactory.getInstance().getUnitOwner(attacked).isNotEndPlaying()) {
        //     Logger.toastError(`${haflHurt}`)
        // }

        if (haflHurt > 0) {
            attacker.damage(attacked, haflHurt, hurtType)
            // if (PlayerFactory.getInstance().getUnitOwner(attacker).isNotEndPlaying()) {
            //     Logger.toastError(`矫正掉落更多的生命值: ${haflHurt}`)
            // }
        } else if (haflHurt < 0) {
            const currentLife = Util.getTargetAttributeEnumByNotHero(attacked, AttributeEnum.life, true);
            // if (PlayerFactory.getInstance().getUnitOwner(attacker).isNotEndPlaying()) {
            //     Logger.toastError(`当前生命值: ${currentLife}`);
            //     Logger.toastError(`矫正回复的生命值: ${haflHurt}`)
            // }
            const maxLife = Util.getTargetAttributeEnumByNotHero(attacked, AttributeEnum.maxLife, true);
            let newLife = currentLife - haflHurt;
            if (newLife >= maxLife) {
                newLife = maxLife;
            }
            // Logger.toastError(`新生命值: ${newLife}`);
            Util.setTargetAttributeEnumToNotHero(attacked, AttributeEnum.life, newLife);
        }
        if (!CodeUtil.isNullOrZero(bloodSuck)) {
            const currentLife = Util.getTargetAttributeEnumByNotHero(attacker, AttributeEnum.life, true);
            const newLife = currentLife + bloodSuck;
            Util.setTargetAttributeEnumToNotHero(attacker, AttributeEnum.life, newLife);
        }
        if (!CodeUtil.isNullOrZero(thorns)) {
            const currentLife = Util.getTargetAttributeEnumByNotHero(attacker, AttributeEnum.life, true);
            const newLife = currentLife - thorns;
            Util.setTargetAttributeEnumToNotHero(attacker, AttributeEnum.life, newLife);
        }

        return retHurt;
    }

    static getAttrToGetHurt = (targetAttr: AttributeEnum, currentAttr: UnitAttribute, unit: UnitModel) => {
        switch (targetAttr) {
            case AttributeEnum.mainAttr: {
                const value = UnitUtil.getMainAttr(unit, currentAttr);
                return value;
            }
        }
        return Util.getTargetAttributeEnumByHero(targetAttr, currentAttr, unit);
    }

    static getMainAttr(unit: UnitModel, currentAttr: UnitAttribute) {
        const unitId = unit?.strId;
        const unitData = DataRouter.getUnitDataByUnitId(unitId);
        let value = 0;
        if (unitData) {
            const mainAttr = unitData?.Primary;
            switch (mainAttr) {
                case MainAttr.AGI:{
                    value = currentAttr[AttributeEnum.agile];
                    break;
                }
                case MainAttr.STR:{
                    value = currentAttr[AttributeEnum.strength];
                    break;
                }
                case MainAttr.INT:{
                    value = currentAttr[AttributeEnum.intellect];
                    break;
                }
            }
        }
        return value;
    }

    static abilityHurtGetValue = (activeUnit: UnitModel, currentAttr: UnitAttribute, targetAttr: UnitAttribute, hurtSourceAttr: AttributeEnum, percentage: number, value: number, hurtSourceIsMine: number): number => {
        const player = PlayerFactory.getInstance().getUnitOwner(activeUnit);
        if (player?.isValid()) {
            Logger.toastSkill(`获取技能伤害值：start!`, player)
            let hurtValue = value;
            Logger.toastSkill(`技能伤害初始值：${hurtValue}`, player)
            if (!CodeUtil.isNullOrZero(hurtSourceAttr)) {
                if (!CodeUtil.isNullOrZero(hurtSourceIsMine)) {
                    hurtValue = UnitUtil.getAttrToGetHurt(hurtSourceAttr, currentAttr, activeUnit);
                } else {
                    hurtValue = UnitUtil.getAttrToGetHurt(hurtSourceAttr, targetAttr, activeUnit);
                }
            }
            Logger.toastSkill(`伤害源: ${hurtValue}`, player)
            if (!CodeUtil.isNullOrZero(percentage)) {
                const percentage = value / 100;
                hurtValue = hurtValue * percentage;
                Logger.toastSkill(`伤害倍数: ${percentage}`, player)
            }
            // if (cardinal && cardinal > 0) {
            //     let constant = cardinal;
            //     if (divisor && divisor > 0) {
            //         constant = constant * (divisor / 100);
            //     }
            //     hurtValue = hurtValue + constant;
            // }
            Logger.toastSkill(`伤害值: ${hurtValue}`, player)
            Logger.toastSkill(`最终伤害: ${hurtValue}`, player)
            Logger.toastSkill(`获取技能伤害值：end`, player)
            return hurtValue;
        }
        return 0;
    }

    static abilityHurtGetValueByEffectData = (activeUnit: UnitModel, targetUnit: UnitModel | void, currentAttr: UnitAttribute, targetAttr: UnitAttribute, effectId: string, effectLevel: number): number => {
        let hurtValue = 0;
        const player = PlayerFactory.getInstance().getUnitOwner(activeUnit);
        if (player?.isValid()) {
        } else {
            return hurtValue;
        }
        if (effectId) {
        } else {
            return hurtValue;
        }
        const abilityEffectData = DataRouter.getEffectByEffectId(effectId);
        if (abilityEffectData) {
            const hurtSourceAttrs = abilityEffectData.harm;
            const hurtSourceAttrArr = CodeUtil.getArrBySplit(hurtSourceAttrs);
            const hurtSourceIsMines = abilityEffectData.harm_source_isMine;
            const hurtSourceIsMineArr = CodeUtil.getArrBySplit(hurtSourceIsMines);
            const harmPercentages = abilityEffectData.harm_percentage;
            const harmPercentagesArr = CodeUtil.getArrBySplit(harmPercentages);
            const harmValues = abilityEffectData.harm_value;
            const harmValueArr = CodeUtil.getArrBySplit(harmValues);

            const harmTargetAttribute = abilityEffectData.harm_target_attribute;


            // 只有伤害目标属性不为0时才需要去计算伤害
            if (harmTargetAttribute && harmTargetAttribute >= 0) {
                for (let i = 0; i < harmValueArr.length; i++) {
                    const hurtSourceAttr = DataRouterUtil.getNumByArr(hurtSourceAttrArr, i);
                    const harmPercentage = DataRouterUtil.getNumByArr(harmPercentagesArr, i);
                    const harmValue = DataRouterUtil.getNumByArr(harmValueArr, i);

                    const hurtSourceIsMine = DataRouterUtil.getNumByArr(hurtSourceIsMineArr, i);
                    let newHurtValue = 0;
                    newHurtValue = UnitUtil.abilityHurtGetValue
                    (
                        activeUnit,
                        currentAttr,
                        targetAttr,
                        hurtSourceAttr,
                        harmPercentage,
                        harmValue,
                        hurtSourceIsMine,
                    )
                    hurtValue = hurtValue + newHurtValue;
                }
            }
            if (!CodeUtil.isNullOrZero(abilityEffectData.upper_limit)) {
                if (hurtValue > abilityEffectData.upper_limit) {
                    hurtValue = abilityEffectData.upper_limit;
                    Logger.toastSkill(`超过伤害上限!`, player)
                }
            }
        } else {
            Logger.toastError(`无效的技能效果表来计算伤害 ${effectId}`)
        }

        return hurtValue * effectLevel;
    }

    static attackToast(attacker: UnitModel, attacked: UnitModel) {
        const attackedId = attacked?.strId;
        const unitData = DataRouter.getUnitDataByUnitId(attackedId);
        const damageTip = unitData?.damageTip;
        if (damageTip) {
            const player = PlayerFactory.getInstance().getUnitOwner(attacked);
            Logger.toastProduct(damageTip, player);
        }
    }

    static getClearFightTimesByPlayer (player: PlayerModel) {
        if (player?.isValid()) {
            const clearFightTime = player.data[PlayerDataKey.clearFightTime];
            return clearFightTime;
        }
        return {};
    }

    static addClearFightTimesByPlayerUnit (player: PlayerModel, unit: UnitModel, time: number) {
        const map = UnitUtil.getClearFightTimesByPlayer(player);
        const size = CodeUtil.getObjSize(map);
        if (size > 0) {
        } else {
            Logger.toastProduct(CodeUtil.getColorStr(ColorStr.red, '进入战斗'), player)
        }
        const handleId = unit.getHandleId();
        map[handleId] = time;
    }

    static caluUnitFightValue(unit: UnitModel, attr: UnitAttribute) {
        const ret = UnitUtil.getUnitHurt(unit, attr[AttributeEnum.attack], attr, new UnitAttribute(), HurtType.physics)
        const hurt = ret.hurt;
        const thorns = ret.thorns;
        const bloodSuck = ret.bloodSuck;
        const value = attr[AttributeEnum.maxLife] * ((hurt + bloodSuck + thorns) * attr[AttributeEnum.attackSpeed])
        return value;
    }

    /**
     * 判断目标单位是否在攻击单位的溅射角度之内
     * @param attackerPos 攻击单位位置
     * @param targetPos 目标单位位置
     * @param attackerDir 攻击单位面向角度
     * @param splashAngle 溅射角度
     * @returns 目标单位是否在攻击单位的溅射角度之内
     */
    static isTargetInSplashRange(attackerPos: LocModel, targetPos: LocModel, attackerDir: number, splashAngle: number): boolean {
        // 计算攻击单位与目标单位之间的向量
        const targetDir = { x: targetPos.x - attackerPos.x, y: targetPos.y - attackerPos.y };

        // 计算攻击单位与目标单位之间的角度（弧度）
        const angle = Math.atan2(targetDir.y, targetDir.x);

        // 将角度转换为度数
        const degree = angle * 180 / Math.PI;

        // 计算攻击单位的面向角度与目标单位之间的夹角（度数）
        const diff = degree - attackerDir;

        // 将夹角控制在 -180 到 180 度之间
        let angleDiff = ((diff % 360) + 360) % 360;
        if (angleDiff > 180) {
            angleDiff = 360 - angleDiff;
        }

        // 判断目标单位是否在攻击单位的溅射角度之内
        return angleDiff <= (splashAngle / 2);
    }

    static customFight (attacker: UnitModel, attacked: UnitModel, attackerPlayer: PlayerModel, attackedPlayer: PlayerModel, damage: number, hurtType: HurtType) {
        Logger.toastSys(`----------------------------------`);
        Logger.toastSys(`自定义战斗系统`);
        // 漂浮文字
        Logger.toastSys(`原始伤害值${damage}`);
        if (!CodeUtil.isNullOrZero(damage)) {
        } else {
            return;
        }

        let hurtNum = damage;


        const attackedRet = UnitUtil.getAttribute(attacked);
        const attackerRet = UnitUtil.getAttribute(attacker);
        const attackedAttribute = attackedRet.newestAttribute;
        const attackerAttribute = attackerRet.newestAttribute;
        if (attackedAttribute && attackerAttribute) {
        } else {
            Logger.toastWarn(`受到未知攻击！`);
            return;
        }
        const attackNum = attackerAttribute[AttributeEnum.attack];
        if (hurtType === HurtType.hero) {
            // Logger.toastError(`原始伤害值${damage}`);
            // 普通攻击
            let ret = UnitUtil.getUnitHurt(attacker, attackNum, attackerAttribute, attackedAttribute, HurtType.physics);
            hurtNum = ret.hurt;
            const bCh = ret.bCh;
            const bHit = ret.bHit;
            const bloodSuck = ret.bloodSuck;
            const bBlock = ret.bBlock;
            const thorns = ret.thorns;
            const realH = ret.realH;
            Logger.toastSys(`hurt： ${hurtNum}`);
            Logger.toastSys(`ch： ${bCh}`);
            Logger.toastSys(`bHit： ${bHit}`);
            UnitUtil.attackCallBack.map(
                (value, index, array) => {
                    value && value(attacker, attacked, bCh, bHit);
                }
            );
            const radius = attackerAttribute[AttributeEnum.attackRange];
            const loc = attacked.loc;

            const arr: UnitModel[] = [];

            let attackRangeAngle = 90;
            if (attackerAttribute[AttributeEnum.attackRangeAngle]) {
                attackRangeAngle = attackerAttribute[AttributeEnum.attackRangeAngle] + attackRangeAngle;
            }

            ModelFactory.getInstance().mapUnitInRange(loc, radius,
                (unit) => {
                    const tempPlayer = PlayerFactory.getInstance().getUnitOwner(unit)
                    const isUnitAlly = attackerPlayer.isPlayerAlly(tempPlayer);
                    const isAlive = DataRouterUtil.checkUnitUseAttr(unit);
                    const isAttacked = (unit === attacked);
                    let bAngle = false;

                    const face = attacker.facing;

                    if (!CodeUtil.isNullOrZero(attackRangeAngle)) {
                        if (UnitUtil.isTargetInSplashRange(attacker.loc, unit.loc, face, attackRangeAngle)) {
                            bAngle = true;
                        }
                    }


                    if(!isUnitAlly && isAlive && !isAttacked && bAngle) {
                        arr.push(unit);
                    }
                }
            )

            arr.push(attacked);

            Logger.toastSys(`arr： ${arr.length}`);
            for (let index = 0; index < arr.length; index ++) {
                const currentUnit = arr[index];
                if (DataRouterUtil.checkUnitUseAttr(currentUnit)) {
                } else {
                    continue;
                }

                if (currentUnit === attacked) {
                    UnitUtil.showHurtNum(hurtNum, attacker, currentUnit, HurtType.physics, bCh, bHit, bBlock, bloodSuck, thorns, realH);
                    hurtNum = UnitUtil.correctHurt(attacker, attacked, HurtType.physics, hurtNum, damage, bloodSuck, bHit, thorns);
                } else {
                    let tempHurtNum = hurtNum;
                    let attackRangeHurt = 0;
                    if (attackerAttribute[AttributeEnum.attackRangeHurt]) {
                        attackRangeHurt = attackerAttribute[AttributeEnum.attackRangeHurt] + attackRangeHurt;
                    }
                    if (!CodeUtil.isNullOrZero(attackRangeHurt)) {
                        tempHurtNum = tempHurtNum * ((attackRangeHurt) / 100)
                        UnitUtil.showHurtNum(tempHurtNum, attacker, currentUnit, HurtType.physics, bCh, bHit, bBlock, bloodSuck, thorns, realH);
                        UnitUtil.correctHurt(attacker, currentUnit, HurtType.physics, tempHurtNum, 0, bloodSuck, bHit, thorns);
                    }
                }
                // todo xlsx 暴击特效
                // todo xlsx 范围攻击特效
            }
        } else {
            // 技能马甲判定
            // 马甲伤害阶段不暴击
            const vestJson = UnitUtil.getVestJson(attacker);
            if (vestJson) {
                Logger.toastSys(`是由马甲扔出来的技能`);
                const effectId = vestJson.effectId;
                const effectLevel = vestJson.effectLevel;
                attacker = vestJson.sourceUnit
                // 马甲技能当主体死亡后就不再使用
                if (attacker?.isAliveInBattle()) {
                } else {
                    return;
                }
                const vestHurtType = vestJson.hurtType;

                const attackerRet = UnitUtil.getAttribute(attacker);
                const attackerAttribute = attackerRet.newestAttribute;


                const hurtValue = UnitUtil.abilityHurtGetValueByEffectData(attacker, attacked, attackerAttribute, attackedAttribute, effectId, effectLevel)
                // 这里造成伤害后会走到自定义战斗系统中, 自定义战斗系统对伤害进行计算暴击与伤害显示
                const ret = UnitUtil.getUnitHurt(attacker, hurtValue, attackerAttribute, attackedAttribute, vestHurtType);
                hurtNum = ret.hurt;
                const bCh = ret.bCh;
                const bHit = ret.bHit;
                const bBlock = ret.bBlock;
                const bloodSuck = ret.bloodSuck;
                const thorns = ret.thorns;
                const realH = ret.realH;
                UnitUtil.showHurtNum(hurtNum, attacker, attacked, vestHurtType, bCh, bHit, bBlock, bloodSuck, thorns, realH);
                hurtNum = UnitUtil.correctHurt(attacker, attacked, vestHurtType, hurtNum, 1, bloodSuck, bHit, thorns);

                Logger.toastSys(`马甲技能伤害${hurtValue}`);
            }
        }

        UnitUtil.attackToast(attacker, attacked);

        let hatredNum = UnitUtil.getHatredNumByHurt(hurtNum);
        if (hatredNum !== 0) {
            const attackerId = attacker.getHandleId();
            const attackedId = attacked.getHandleId();
            attacker.data[UnitDataKey.hatredAttackerMap][attackedId] = true;

            if (!CodeUtil.isNullOrZero(attacked.data[UnitDataKey.hatredAttackedMap][attackerId])) {
                attacked.data[UnitDataKey.hatredAttackedMap][attackerId] = attacked.data[UnitDataKey.hatredAttackedMap][attackerId] + hatredNum;
            } else {
                attacked.data[UnitDataKey.hatredAttackedMap][attackerId] = hatredNum;
            }
        }

        if (hurtNum > 0) {
            const intHurt = MathUtil.round(hurtNum)
            if (attackerPlayer.data[PlayerDataKey.hurtNum] >= 0) {
                attackerPlayer.data[PlayerDataKey.hurtNum] = attackerPlayer.data[PlayerDataKey.hurtNum]  + intHurt;
            }
            if (attacker.data[UnitDataKey.hurtNum] >= 0) {
                attacker.data[UnitDataKey.hurtNum] = attacker.data[UnitDataKey.hurtNum]  + intHurt;
            }

            const unitData = DataRouterUtil.getUnitDataByUnit(attacked);
            switch (unitData?.unit_type) {
                case UnitType.shareBoss: {
                    GlobalEnv.shareBossLife = GlobalEnv.shareBossLife - intHurt;
                    break;
                }
                case UnitType.boss: {
                    attackedPlayer.data[PlayerDataKey.lastHurtBoss] = attacked;
                    break;
                }
            }

            if (attackedPlayer?.isNotEndPlaying()) {
            } else {
                // 目标点特效
                const point_effects: string = Configer.getResource(ResourceKey.attackedEffect);
                const point_effects_scale: number = Configer.getConfig(ConfigKey.attackedEffectScale);
                const point_effects_time = 3;
                let loc = attacked.loc;
                if (point_effects) {
                    TimeController.addSpecialEffectFun(attacker, point_effects, point_effects_time, loc, undefined, SpecialEffectType.point, point_effects_scale)
                }
            }
        }

        const removeFightTime = Configer.getConfig(ConfigKey.removeFightTime) as number;
        UnitUtil.addClearFightTimesByPlayerUnit(attackerPlayer, attacker, removeFightTime)
        UnitUtil.addClearFightTimesByPlayerUnit(attackedPlayer, attacked, removeFightTime)
        Logger.toastSys(`----------------------------------`);
    }

    static getHatredNumByHurt(hurt: number) {
        const hatredNum = MathUtil.abs(hurt);
        return hatredNum;
    }

    static addCoin(unit: UnitModel, player: PlayerModel, coin: number) {
        const oldCoin = player.gold;
        const newGoin = oldCoin + coin;
        player.gold = newGoin;

        let fontSize = 25;
        let scaleRadix = 0.7;
        const showTime = 0.1;
        const coinStr = `${MathUtil.floor(coin)}`;
        UnitUtil.showNumModelFun(unit, ResourceKey.chGoldChars, ResourceKey.goldChars, ResourceKey.abbrGoldChars, coinStr, fontSize, unit.loc, showTime, scaleRadix);
    }

    static addWood(unit: UnitModel, player: PlayerModel, wood: number) {
        const oldWood = player.lumber;
        const newWood = oldWood + wood;
        player.lumber = newWood;

        let fontSize = 25;
        let scaleRadix = 0.7;
        const showTime = 0.1;

        const woodStr = `${MathUtil.floor(wood)}`;
        UnitUtil.showNumModelFun(unit, ResourceKey.chWoodChars, ResourceKey.woodChars, ResourceKey.abbrWoodChars, woodStr, fontSize, unit.loc, showTime, scaleRadix);
    }
    //
    // static checkUseAbility (): UseAbilityFailedType {
    //
    // }

}